home *** CD-ROM | disk | FTP | other *** search
- PROGRAM SortDemo;
-
- { Graphical demonstration of sorting algorithms (W. N~ker, 02/96) }
- { based on "Sortieren" of Purity #48 }
-
- {
- Translated to PCQ from Kick(Maxon) Pascal.
- Updated the source to 2.0+.
- Now uses GadTools for menus.
- Added CloseWindowSafely.
- Cleaned up the menuhandling.
- Added LockWinSize and RestoreWin, now the
- window will be locked on showtime.
-
- The German text was translated to English
- by Andreas Neumann, thanks Andreas.
- Jun 03 1998.
-
- Translated to FPC Pascal.
- Removed CloseWindowSafely, have do add
- that procedure to Intuition.
- Fixed a bug, when you halt the show the
- window stayed locked.
- Aug 23 1998.
-
- Added MessageBox for report.
- 31 Jul 2000.
-
- nils.sjoholm@mailbox.swipnet.se
-
- One last remark, the heapsort can't be stoped
- so you have to wait until it's finished.
- }
-
- uses Exec, Intuition, Graphics, Utility, GadTools, vartags,msgbox;
-
-
-
- CONST version : PChar = '$VER: SortDemo 1.3 (23-Aug-98)';
-
- nmax=2000;
-
- MinWinX = 80;
- MinWiny = 80;
-
- w : pWindow = Nil;
- s : pScreen = Nil;
- MenuStrip : pMenu = Nil;
- vi : Pointer = Nil;
-
-
- modenames : Array[0..7] of string[10] = (
- 'Heapsort',
- 'Shellsort',
- 'Pick out',
- 'Insert',
- 'Shakersort',
- 'Bubblesort',
- 'Quicksort',
- 'Mergesort');
-
- { The easiest way to use gadtoolsmenus in FPC is
- to have them as const types. No need to cast
- strings to PChar. That we have to use recordmembers
- name is a pain.
- }
-
- nm : array[0..21] of tNewMenu = (
- (nm_Type: NM_TITLE; nm_Label: 'Demo';nm_CommKey: NIL; nm_Flags: 0;
- nm_MutualExclude: 0; nm_UserData: NIL),
- (nm_Type: NM_ITEM; nm_Label: 'Start';nm_CommKey: 'S'; nm_Flags: 0;
- nm_MutualExclude: 0; nm_UserData: NIL),
- (nm_Type: NM_ITEM; nm_Label: 'Stop';nm_CommKey: 'H'; nm_Flags: 0;
- nm_MutualExclude: 0; nm_UserData: NIL),
-
- { this will be a barlabel, have to set this one later }
- (nm_Type: NM_ITEM; nm_Label: NIL; nm_CommKey: NIL; nm_Flags: 0;
- nm_MutualExclude: 0; nm_UserData: NIL),
-
- (nm_Type: NM_ITEM; nm_Label: 'Quit'; nm_CommKey: 'Q'; nm_Flags: 0;
- nm_MutualExclude: 0; nm_UserData: NIL),
- (nm_Type: NM_TITLE; nm_Label: 'Algorithm'; nm_CommKey: NIL; nm_Flags: 0;
- nm_MutualExclude: 0; nm_UserData: NIL),
- (nm_Type: NM_ITEM; nm_Label: 'HeapSort'; nm_CommKey: '1'; nm_Flags:
- CHECKIT+CHECKED+MENUTOGGLE; nm_MutualExclude: 254; nm_UserData: NIL),
- (nm_Type: NM_ITEM; nm_Label: 'ShellSort'; nm_CommKey: '2'; nm_Flags:
- CHECKIT+MENUTOGGLE; nm_MutualExclude: 253; nm_UserData: NIL),
- (nm_Type: NM_ITEM; nm_Label: 'Pick out'; nm_CommKey: '3'; nm_Flags:
- CHECKIT+MENUTOGGLE; nm_MutualExclude: 251; nm_UserData: NIL),
- (nm_Type: NM_ITEM; nm_Label: 'Insert'; nm_CommKey: '4'; nm_Flags:
- CHECKIT+MENUTOGGLE; nm_MutualExclude: 247; nm_UserData: NIL),
- (nm_Type: NM_ITEM; nm_Label: 'ShakerSort'; nm_CommKey: '5'; nm_Flags:
- CHECKIT+MENUTOGGLE; nm_MutualExclude: 239; nm_UserData: NIL),
- (nm_Type: NM_ITEM; nm_Label: 'BubbleSort'; nm_CommKey: '6'; nm_Flags:
- CHECKIT+MENUTOGGLE; nm_MutualExclude: 223; nm_UserData: NIL),
- (nm_Type: NM_ITEM; nm_Label: 'QuickSort'; nm_CommKey: '7'; nm_Flags:
- CHECKIT+MENUTOGGLE; nm_MutualExclude: 191; nm_UserData: NIL),
- (nm_Type: NM_ITEM; nm_Label: 'MergeSort'; nm_CommKey: '8'; nm_Flags:
- CHECKIT+MENUTOGGLE; nm_MutualExclude: 127; nm_UserData: NIL),
- (nm_Type: NM_TITLE; nm_Label: 'Preferences'; nm_CommKey: NIL; nm_Flags: 0;
- nm_MutualExclude: 0; nm_UserData: NIL),
- (nm_Type: NM_ITEM; nm_Label: 'Data'; nm_CommKey: NIL; nm_Flags: 0;
- nm_MutualExclude: 0; nm_UserData: NIL),
- (nm_Type: NM_SUB; nm_Label: 'Random'; nm_CommKey: 'R'; nm_Flags:
- CHECKIT+CHECKED+MENUTOGGLE; nm_MutualExclude: 2; nm_UserData: NIL),
- (nm_Type: NM_SUB; nm_Label: 'Malicious'; nm_CommKey: 'M'; nm_Flags:
- CHECKIT+MENUTOGGLE; nm_MutualExclude: 1; nm_UserData: NIL),
- (nm_Type: NM_ITEM; nm_Label: 'Diagram'; nm_CommKey: NIL; nm_Flags: 0;
- nm_MutualExclude: 0; nm_UserData: NIL),
- (nm_Type: NM_SUB; nm_Label: 'Needles'; nm_CommKey: 'N'; nm_Flags:
- CHECKIT+CHECKED+MENUTOGGLE; nm_MutualExclude: 2; nm_UserData: NIL),
- (nm_Type: NM_SUB; nm_Label: 'Dots'; nm_CommKey: 'D'; nm_Flags:
- CHECKIT+MENUTOGGLE; nm_MutualExclude: 1; nm_UserData: NIL),
- (nm_Type: NM_END; nm_Label: NIL; nm_CommKey: NIL; nm_Flags:
- 0;nm_MutualExclude:0;nm_UserData:NIL));
-
-
- VAR sort: ARRAY[1..nmax] OF Real;
- sort2: ARRAY[1..nmax] OF Real; { for dumb Mergesort %-( }
- num,range,modus : Integer;
- rndom,needles : Boolean;
- Rast : pRastPort;
- QuitStopDie : Boolean;
- Msg : pMessage;
- wintitle : string[80];
- scrtitle : string[80];
-
- Procedure CleanUp(s : string; err : Integer);
- begin
- if assigned(MenuStrip) then begin
- ClearMenuStrip(w);
- FreeMenus(MenuStrip);
- end;
- if assigned(vi) then FreeVisualInfo(vi);
- if assigned(w) then CloseWindow(w);
- if assigned(GfxBase) then CloseLibrary(GfxBase);
- if assigned(GadToolsBase) then CloseLibrary(GadToolsBase);
- if s <> '' then MessageBox('SortDemo Report',s,'OK');
- Halt(err);
- end;
-
- Procedure RestoreWin;
- var
- dummy : Boolean;
- begin
- dummy := WindowLimits(w,MinWinX,MinWinY,-1,-1);
- end;
-
- Procedure LockWinSize(x,y,x2,y2 : Integer);
- var
- dummy : Boolean;
- begin
- dummy := WindowLimits(w,x,y,x2,y2);
- end;
-
- FUNCTION cancel: Boolean;
- { checked while sorting }
- VAR m,i,s: Integer;
- result : boolean;
- IM : pIntuiMessage;
- BEGIN
- result := False;
- IM := pIntuiMessage(GetMsg(w^.UserPort));
- IF IM<>Nil THEN BEGIN
- IF IM^.IClass=IDCMP_CLOSEWINDOW THEN
- result := True; { Close-Gadget }
- IF IM^.IClass=IDCMP_MENUPICK THEN BEGIN
- m := IM^.Code AND $1F;
- i := (IM^.Code SHR 5) AND $3F;
- s := (IM^.Code SHR 11) AND $1F;
- IF (m=0) AND (i=1) THEN result := True; { Menu item "Stop" }
- END;
- ReplyMsg(pMessage(Msg));
- END;
- cancel := result;
- END;
-
-
- PROCEDURE showstack(size: Integer);
- { little diagram showing the depth of Quicksort's recursion :-) }
- BEGIN
- SetAPen(Rast,2); IF size>0 THEN RectFill(Rast,0,0,3,size-1);
- SetAPen(Rast,0); RectFill(Rast,0,size,3,size);
- END;
-
-
- PROCEDURE setpixel(i: Integer);
- BEGIN
- SetAPen(Rast,1);
- IF needles THEN BEGIN
- Move(Rast,i,range); Draw(Rast,i,Round((1-sort[i])*range));
- END ELSE
- IF WritePixel(Rast,i,Round((1-sort[i])*range))=0 THEN;
- END;
-
- PROCEDURE clearpixel(i: Integer);
- BEGIN
- SetAPen(Rast,0);
- IF needles THEN BEGIN
- Move(Rast,i,range); Draw(Rast,i,Round((1-sort[i])*range));
- END ELSE
- IF WritePixel(Rast,i,Round((1-sort[i])*range))=0 THEN;
- END;
-
- procedure Exchange(var first,second : real);
- var
- temp : real;
- begin
- temp := first;
- first := second;
- second := temp;
- end;
-
- PROCEDURE swapit(i,j: integer);
- BEGIN
- clearpixel(i); clearpixel(j);
- Exchange(sort[i],sort[j]);
- setpixel(i); setpixel(j);
- END;
-
- FUNCTION descending(i,j: Integer): Boolean;
- BEGIN
- descending := sort[i]>sort[j];
- END;
-
- Function IntToStr (I : Longint) : String;
-
- Var S : String;
-
- begin
- Str (I,S);
- IntToStr:=S;
- end;
-
-
- PROCEDURE settitles(time: Longint);
- VAR
- s : string[80];
- BEGIN
- s := modenames[modus];
- IF time=0 THEN
- wintitle := s + ' running ...'
- ELSE IF time < 0 then
- wintitle := '<- ' + IntToStr(num) + ' Data ->'
- ELSE
- wintitle := IntToStr(time) + ' Seconds';
- scrtitle := strpas(@version[6]) + ' - ' + s;
- wintitle := wintitle + #0;
- scrtitle := scrtitle + #0;
- SetWindowTitles(w,@wintitle[1],@scrtitle[1]);
- END;
-
- PROCEDURE refresh;
- { react on new size of window/init data }
- VAR i: Integer;
- BEGIN
- num := w^.GZZWidth; IF num>nmax THEN num := nmax;
- range := w^.GZZHeight;
- settitles(-1);
- SetRast(Rast,0); { clear screen }
- FOR i := 1 TO num DO BEGIN
- IF rndom THEN sort[i] := Random { produces 0..1 }
- ELSE sort[i] := (num-i)/num;
- setpixel(i);
- END;
- END;
-
- { *#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#* }
- { *#*#*#*#*#*#*#*#*#*#*# The sorting algorithms! #*#*#*#*#*#*#*#*#*#*#*#* }
- { *#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#* }
-
- PROCEDURE bubblesort;
- { like the head of a beer, reaaal slow and easy-going }
- VAR i,j,max: Integer;
- BEGIN
- LockWinSize(w^.Width,w^.Height,w^.Width,w^.Height);
- max := num;
- REPEAT
- j := 1;
- FOR i := 1 TO max-1 DO
- IF descending(i,i+1) THEN BEGIN
- swapit(i,i+1); j := i;
- END;
- max := j;
- UNTIL (max=1) OR cancel;
- RestoreWin;
- END;
-
- PROCEDURE shakersort;
- { interesting variant, but bubblesort still remains hopelessness }
- { (because it only compares and swaps immediate adjacent units) }
- VAR i,j,min,max: Integer;
- BEGIN
- LockWinSize(w^.Width,w^.Height,w^.Width,w^.Height);
- min := 1;
- max := num;
- REPEAT
- j := min;
- FOR i := min TO max-1 DO
- IF descending(i,i+1) THEN BEGIN
- swapit(i,i+1); j := i;
- END;
- max := j;
- j := max;
- FOR i := max DOWNTO min+1 DO
- IF descending(i-1,i) THEN BEGIN
- swapit(i,i-1); j := i;
- END;
- min := j;
- UNTIL (max=min) OR cancel;
- RestoreWin;
- END;
-
- PROCEDURE e_sort;
- { Insert: a pretty human strategy }
- VAR i,j: Integer;
- BEGIN
- LockWinSize(w^.Width,w^.Height,w^.Width,w^.Height);
- FOR i := 2 TO num DO BEGIN
- j := i;
- WHILE j>1 DO
- IF descending(j-1,j) THEN BEGIN
- swapit(j-1,j); Dec(j);
- END ELSE
- j := 1;
- IF cancel THEN begin
- RestoreWin;
- Exit;
- end;
- END;
- RestoreWin;
- END;
-
- PROCEDURE a_sort;
- { Pick out: Preparation is one half of a life }
- { Take a look at the ridiculous low percentage of successful comparisions: }
- { Although there are only n swaps, there are n^2/2 comparisions! }
- { Both is a record, one in a good sense, the other one in a bad sense. }
-
- VAR i,j,minpos: Integer;
- min: Real;
- BEGIN
- LockWinSize(w^.Width,w^.Height,w^.Width,w^.Height);
- FOR i := 1 TO num-1 DO BEGIN
- minpos := i; min := sort[i];
- FOR j := i+1 TO num DO
- IF descending(minpos,j) THEN
- minpos := j;
- IF minpos<>i THEN swapit(i,minpos);
- IF cancel THEN begin
- RestoreWin;
- Exit;
- end;
- END;
- RestoreWin;
- END;
-
- PROCEDURE shellsort;
- { brilliant extension of E-Sort, stunning improvement of efficience }
- VAR i,j,gap: Integer;
- BEGIN
- LockWinSize(w^.Width,w^.Height,w^.Width,w^.Height);
- gap := num DIV 2;
- REPEAT
- FOR i := 1+gap TO num DO BEGIN
- j := i;
- WHILE j>gap DO
- IF descending(j-gap,j) THEN BEGIN
- swapit(j,j-gap); j := j-gap;
- END ELSE
- j := 1;
- IF cancel THEN begin
- RestoreWin;
- Exit;
- end;
- END;
- gap := gap DIV 2;
- UNTIL gap=0;
- RestoreWin;
- END;
-
- PROCEDURE seepaway(i,max: Integer);
- { belongs to heapsort }
- VAR j: Integer;
- BEGIN
- j := 2*i;
- WHILE j<=max DO BEGIN
- IF j<max THEN IF descending(j+1,j) THEN
- Inc(j);
- IF descending(j,i) THEN BEGIN
- swapit(j,i);
- i := j; j := 2*i;
- END ELSE
- j := max+1; { cancels }
- END;
- END;
-
- PROCEDURE heapsort;
- { this genius rules over the chaos: it's the best algorithm, I know about }
- { The only disadvantage compared with shellsort: it's not easy to understand }
- { and impossible to know it by heart. }
- VAR i,j: Integer;
- BEGIN
- LockWinSize(w^.Width,w^.Height,w^.Width,w^.Height);
- i := num DIV 2 + 1;
- j := num;
- WHILE i>1 DO BEGIN
- Dec(i); seepaway(i,j);
- END;
- WHILE j>1 DO BEGIN
- swapit(i,j);
- Dec(j); seepaway(i,j);
- END;
- RestoreWin;
- END;
-
- PROCEDURE quicksort;
- { "divide and rule": a classic, but recursive >>-( }
- { In this demonstration it is faster than heapsort, but does considerable }
- { more unsuccessful comparisions. }
- VAR stack: ARRAY[1..100] OF RECORD li,re: Integer; END;
- sp,l,r,m,i,j: Integer;
- BEGIN
- LockWinSize(w^.Width,w^.Height,w^.Width,w^.Height);
- sp := 1; stack[1].li := 1; stack[1].re := num;
- REPEAT
- l := stack[sp].li; r := stack[sp].re; Dec(sp);
- showstack(sp);
- m := (l+r) DIV 2;
- i := l; j := r;
- REPEAT
- WHILE descending(m,i) DO Inc(i);
- WHILE descending(j,m) DO Dec(j);
- IF j>i THEN swapit(i,j);
- IF m=i THEN m := j ELSE IF m=j THEN m := i; { ahem ... }
- { This "Following" of the reference data is only required because }
- { I stubborn call the comparision function, and this one only gets }
- { indices on the values which have to be compared. }
- UNTIL i>=j;
- IF i>l THEN BEGIN
- Inc(sp); stack[sp].li := l; stack[sp].re := i; END;
- IF i+1<r THEN BEGIN
- Inc(sp); stack[sp].li := i+1; stack[sp].re := r; END;
- UNTIL (sp=0) OR cancel;
- RestoreWin;
- END;
-
- PROCEDURE mergesort;
- { *the* algorithm for lists with pointers on it, for arrays rather }
- { inacceptable. The non.recursive implementation came out pretty more }
- { complicated than the one for quicksort, as quicksort first does }
- { something and then recurses; with mergesort it is the other way round. }
- VAR stack: ARRAY[1..100] OF RECORD li,re,mi: Integer; END;
- sp,l,r,i,j,k,m: Integer;
- BEGIN
- LockWinSize(w^.Width,w^.Height,w^.Width,w^.Height);
- sp := 1; stack[1].li := 1; stack[1].re := num; stack[1].mi := 0;
- REPEAT
- l := stack[sp].li; r := stack[sp].re; m := stack[sp].mi; Dec(sp);
- showstack(sp);
- IF m>0 THEN BEGIN { put two halfs together }
- { Unfortunately it is only possible in an efficient way by using }
- { extra memory; mergesort really is something for lists with }
- { pointers originally ... }
- FOR i := m DOWNTO l do sort2[i] := sort[i]; i := l;
- FOR j := m+1 TO r DO sort2[r+m+1-j] := sort[j]; j := r;
- FOR k := l TO r DO BEGIN
- clearpixel(k);
- IF sort2[i]<sort2[j] THEN BEGIN
- sort[k] := sort2[i]; Inc(i);
- END ELSE BEGIN
- sort[k] := sort2[j]; Dec(j);
- END;
- setpixel(k);
- END;
- END ELSE IF l<r THEN BEGIN
- { create two halfs and the order to put them together }
- m := (l+r) DIV 2;
- Inc(sp); stack[sp].li := l; stack[sp].mi := m; stack[sp].re := r;
- Inc(sp); stack[sp].li := m+1; stack[sp].mi := 0; stack[sp].re := r;
- Inc(sp); stack[sp].li := l; stack[sp].mi := 0; stack[sp].re := m;
- END;
- UNTIL (sp=0) OR cancel;
- RestoreWin;
- END;
-
-
- Procedure OpenEverything;
- begin
- GadToolsBase := OpenLibrary(GADTOOLSNAME,37);
- if GadToolsBase = nil then CleanUp('Can''t open gadtools.library',20);
- GfxBase := OpenLibrary(GRAPHICSNAME,37);
- if GfxBase = nil then CleanUp('Can''t open graphics.library',20);
-
- s := LockPubScreen(nil);
- if s = nil then CleanUp('Could not lock pubscreen',10);
-
- vi := GetVisualInfoA(s, NIL);
- if vi = nil then CleanUp('No visual info',10);
-
- w := OpenWindowTagList(NIL, TAGS(
- WA_IDCMP, IDCMP_CLOSEWINDOW or IDCMP_MENUPICK or
- IDCMP_NEWSIZE,
- WA_Left, 0,
- WA_Top, s^.BarHeight+1,
- WA_Width, 224,
- WA_Height, s^.Height-(s^.BarHeight-1),
- WA_MinWidth, MinWinX,
- WA_MinHeight, MinWinY,
- WA_MaxWidth, -1,
- WA_MaxHeight, -1,
- WA_DepthGadget, ltrue,
- WA_DragBar, ltrue,
- WA_CloseGadget, ltrue,
- WA_SizeGadget, ltrue,
- WA_Activate, ltrue,
- WA_SizeBRight, ltrue,
- WA_GimmeZeroZero, ltrue,
- WA_PubScreen, longint(s),
- TAG_END));
-
- IF w=NIL THEN CleanUp('Could not open window',20);
-
- Rast := w^.RPort;
-
- { Here we set the barlabel }
- nm[3].nm_Label := PChar(NM_BARLABEL);
-
- if pExecBase(_ExecBase)^.LibNode.Lib_Version >= 39 then begin
- MenuStrip := CreateMenusA(@nm,TAGS(
- GTMN_FrontPen, 1, TAG_END));
- end else MenuStrip := CreateMenusA(@nm,NIL);
-
- if MenuStrip = nil then CleanUp('Could not open Menus',10);
- if LayoutMenusA(MenuStrip,vi,NIL)=false then
- CleanUp('Could not layout Menus',10);
-
- if SetMenuStrip(w, MenuStrip) = false then
- CleanUp('Could not set the Menus',10);
-
- end;
-
- PROCEDURE ProcessIDCMP;
- VAR
- IMessage : tIntuiMessage;
- IPtr : pIntuiMessage;
-
- Procedure ProcessMenu;
- var
- MenuNumber : Integer;
- ItemNumber : Integer;
- SubItemNumber : Integer;
- t0,t1,l : Longint;
-
- begin
- if IMessage.Code = MENUNULL then
- Exit;
-
- MenuNumber := MenuNum(IMessage.Code);
- ItemNumber := ItemNum(IMessage.Code);
- SubItemNumber := SubNum(IMessage.Code);
-
- case MenuNumber of
- 0 : begin
- case ItemNumber of
- 0 : begin
- refresh;
- settitles(0);
- CurrentTime(t0,l);
- CASE modus OF
- 0: heapsort;
- 1: shellsort;
- 2: a_sort;
- 3: e_sort;
- 4: shakersort;
- 5: bubblesort;
- 6: quicksort;
- 7: mergesort;
- END;
- CurrentTime(t1,l);
- settitles(t1-t0);
- end;
- 3 : QuitStopDie := True;
- end;
- end;
- 1 : begin
- case ItemNumber of
- 0..7 : modus := ItemNumber;
- end;
- settitles(-1);
- end;
- 2 : begin
- case ItemNumber of
- 0 : begin
- case SubItemNumber of
- 0 : if not rndom then rndom := true;
- 1 : if rndom then rndom := false;
- end;
- end;
- 1 : begin
- case SubItemNumber of
- 0 : if not needles then needles := true;
- 1 : if needles then needles := false;
- end;
- end;
- end;
- end;
- end;
- end;
-
- begin
- IPtr := pIntuiMessage(Msg);
- IMessage := IPtr^;
- ReplyMsg(Msg);
-
- case IMessage.IClass of
- IDCMP_MENUPICK : ProcessMenu;
- IDCMP_NEWSIZE : refresh;
- IDCMP_CLOSEWINDOW : QuitStopDie := True;
- end;
- end;
-
-
-
- begin
- OpenEverything;
- QuitStopDie := False;
- modus := 0;
- needles := true;
- rndom := true;
- refresh;
- repeat
- Msg := WaitPort(w^.UserPort);
- Msg := GetMsg(w^.UserPort);
- ProcessIDCMP;
- until QuitStopDie;
- CleanUp('',0);
- end.
-
-
-
-